/***************************************************************************
 *   Copyright (C) 2004 by Tim Macfarlane                                  *
 *   timmacfarlane@blueyonder.co.uk                                        *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#ifndef _tycho_parser_h_
#define _tycho_parser_h_

#ifdef RUBY
#include <ruby.h>
#endif

#ifdef JAVA
#include <jni.h>
#endif

#include <vector>

#ifdef WIN32
typedef unsigned int uint;
#endif

using namespace std;

#define TRUE 1
#define FALSE 0

#ifdef JAVA
class NoClassException {
public:
	const char *_clsName;
	NoClassException (const char *clsName);
};

class JavaEnvironment {
	jmethodID _sourceDescriptionConstructor,
			  _sourceDescriptionGetFilename,
			  _sourceDescriptionGetIsFilename,
			  _sourceLocationConstructor,
			  _integerTokenConstructor,
			  _identifierTokenConstructor,
			  _bracketTokenConstructor,
			  _arrayListConstructor,
			  _arrayListAdd,
			  _floatingTokenConstructor,
			  _stringTokenConstructor,
			  _quoteTokenConstructor,
			  _errorTokenConstructor,
			  _versionTokenConstructor1,
			  _versionTokenConstructor2,
			  _versionConstructor;

	jclass _sourceDescriptionClass,
		   _sourceLocationClass,
		   _integerTokenClass,
		   _identifierTokenClass,
		   _bracketTokenClass,
		   _arrayListClass,
		   _floatingTokenClass,
		   _stringTokenClass,
		   _quoteTokenClass,
		   _errorTokenClass,
		   _versionTokenClass,
		   _versionClass,
		   _nullPointerExceptionClass;

	jstring _source;
	jobject _sourceDescription;

	jclass getClass (JNIEnv *env, const char *clsName);
	jmethodID getMethod (JNIEnv *env, jclass cls, const char *name, const char *sig);
	jmethodID getConstructor (JNIEnv *env, jclass cls, const char *sig);
public:
	JNIEnv *_jni;

	JavaEnvironment (JNIEnv *env);

	jstring createString (const char *str);
	const char *getChars (jstring str);
	void releaseChars (jstring str, const char *chars);
	char *setBlockSource (jstring str);
	void setSourceDescription (jobject desc);
	jobject getSourceDescription ();
	jstring getBlockSource ();

	jobject createSourceDescription (jboolean isFilename, jstring filename);
	jstring SourceDescription_GetFilename (jobject srcDesc);
	jboolean SourceDescription_GetIsFilename (jobject srcDesc);
	jobject createSourceLocation (jobject desc, jint rowStart, jint rowEnd, jint colStart, jint colEnd);
	jobject createIntegerToken (jlong value, jobject attrs, jobject sloc);
	jobject createIdentifierToken (jstring id, jobject attrs, jobject sloc);
	jobject createBracketToken (jint type, jobject tokens, jobject attrs, jobject sloc);
	jobject createArrayList ();
	void ArrayList_Add (jobject ary, jobject item);
	jobject createFloatingToken (jdouble value, jobject attrs, jobject sloc);
	jobject createStringToken (jstring value, jobject attrs, jobject sloc);
	jobject createQuoteToken (jstring type, jstring value, jobject attrs, jobject sloc);
	jobject createErrorToken (jstring error, jobject attrs, jobject sloc);
	jobject createVersionToken (jobject version, jobject attrs, jobject sloc);
	jobject createVersionToken (jobject version, jobject toVersion, jstring branch, jobject attrs, jobject sloc);
	jobject createVersion (jint major, jint minor, jint revision, jint build);
	void throwNullPointerException (const char *msg);
};
#endif

class SourceLocation {
private:
	char *_source, *_file_name;
	uint _line_start, _line_end, _col_start, _col_end;
public:

	SourceLocation (uint line_start, uint line_end, uint col_start, uint col_end, char *source, char *file_name);

#ifdef RUBY
	VALUE to_ruby ();
#endif

#ifdef JAVA
	jobject to_java (JavaEnvironment *env);
#endif

#ifdef CLR
	Tycho::Parser::Tokens::SourceLocation ^to_clr ();
#endif
};

class Token {
#ifdef RUBY
	VALUE attributes_to_ruby ();
#endif

#ifdef JAVA
	jobject attributes_to_java (JavaEnvironment *env);
#endif

#ifdef CLR
	System::Collections::Generic::List<Tycho::Parser::Tokens::Token^> ^attributes_to_clr ();
#endif
public:
	vector<Token*> *_attributes;

	Token ();
	virtual ~Token ();

	SourceLocation *_sloc;

#ifdef RUBY
	VALUE to_ruby ();
	virtual VALUE token_to_ruby () = 0;
#endif

#ifdef JAVA
	jobject to_java (JavaEnvironment *env);
	virtual jobject token_to_java (JavaEnvironment *env, jobject attrs, jobject sloc) = 0;
#endif

#ifdef CLR
	Tycho::Parser::Tokens::Token ^to_clr ();
	virtual Tycho::Parser::Tokens::Token ^token_to_clr (System::Collections::Generic::List<Tycho::Parser::Tokens::Token^> ^attrs, Tycho::Parser::Tokens::SourceLocation ^sloc) = 0;
#endif
};

class IntegerToken : public Token {
	int _value;
public:
	IntegerToken (int i);

#ifdef RUBY
	VALUE token_to_ruby ();
#endif

#ifdef JAVA
	jobject token_to_java (JavaEnvironment *env, jobject attrs, jobject sloc);
#endif

#ifdef CLR
	Tycho::Parser::Tokens::Token ^token_to_clr (System::Collections::Generic::List<Tycho::Parser::Tokens::Token^> ^attrs, Tycho::Parser::Tokens::SourceLocation ^sloc);
#endif
};

class IdentifierToken : public Token {
	const char *_id;
	bool _is_operator;
#ifdef RUBY
	VALUE module_to_ruby ();
#endif
public:
	vector<const char*> *_module_path;

	IdentifierToken (const char *i, bool is_op);
	~IdentifierToken ();

#ifdef RUBY
	VALUE token_to_ruby ();
#endif

#ifdef JAVA
	jobject token_to_java (JavaEnvironment *env, jobject attrs, jobject sloc);
#endif

#ifdef CLR
	Tycho::Parser::Tokens::Token ^token_to_clr (System::Collections::Generic::List<Tycho::Parser::Tokens::Token^> ^attrs, Tycho::Parser::Tokens::SourceLocation ^sloc);
	System::Collections::Generic::List<System::String^> ^IdentifierToken::module_to_clr ();
#endif
};

class FloatToken : public Token {
	double _value;
public:
	FloatToken (double v);

#ifdef RUBY
	VALUE token_to_ruby ();
#endif

#ifdef JAVA
	jobject token_to_java (JavaEnvironment *env, jobject attrs, jobject sloc);
#endif

#ifdef CLR
	Tycho::Parser::Tokens::Token ^token_to_clr (System::Collections::Generic::List<Tycho::Parser::Tokens::Token^> ^attrs, Tycho::Parser::Tokens::SourceLocation ^sloc);
#endif
};

class ListToken : public Token {
public:
	enum Type {
		List,
		Brace,
		Bracket,
		Parenthesis
	};
private:
	vector<Token*> *_tokens;

#ifdef RUBY
	VALUE type_to_ruby ();
#endif
public:
	ListToken::Type _type;

	ListToken (ListToken::Type type, vector<Token*> *tokens);
	~ListToken ();

#ifdef RUBY
	VALUE token_to_ruby ();
#endif

#ifdef JAVA
	jobject token_to_java (JavaEnvironment *env, jobject attrs, jobject sloc);
#endif

#ifdef CLR
	Tycho::Parser::Tokens::Token ^token_to_clr (System::Collections::Generic::List<Tycho::Parser::Tokens::Token^> ^attrs, Tycho::Parser::Tokens::SourceLocation ^sloc);
	System::Collections::Generic::List<Tycho::Parser::Tokens::Token^> ^ListToken::to_clr_tokens ();
#endif
};

class StringToken : public Token {
	const char *_label, *_str;
	bool _escaped;
public:
	StringToken (const char *label, const char *str, bool escaped);
	~StringToken ();

#ifdef RUBY
	VALUE token_to_ruby ();
#endif

#ifdef JAVA
	jobject token_to_java (JavaEnvironment *env, jobject attrs, jobject sloc);
#endif

#ifdef CLR
	Tycho::Parser::Tokens::Token ^token_to_clr (System::Collections::Generic::List<Tycho::Parser::Tokens::Token^> ^attrs, Tycho::Parser::Tokens::SourceLocation ^sloc);
#endif
};

class ErrorToken : public Token {
	const char *_code;
public:
	ErrorToken (const char *code);

#ifdef RUBY
	VALUE token_to_ruby ();
#endif

#ifdef JAVA
	jobject token_to_java (JavaEnvironment *env, jobject attrs, jobject sloc);
#endif

#ifdef CLR
	Tycho::Parser::Tokens::Token ^token_to_clr (System::Collections::Generic::List<Tycho::Parser::Tokens::Token^> ^attrs, Tycho::Parser::Tokens::SourceLocation ^sloc);
#endif
};

class Version {
public:
	unsigned short major, minor, revision, build;
	Version ();

#ifdef RUBY
	VALUE to_ruby ();
#endif

#ifdef JAVA
	jobject to_java (JavaEnvironment *env);
#endif

#ifdef CLR
	Tycho::Parser::Tokens::Version ^to_clr ();
#endif
};

class VersionToken : public Token {
public:
	::Version _version, _to_version;
	bool _has_to_version;
	char *_branch;
	VersionToken ();

#ifdef RUBY
	VALUE token_to_ruby ();
#endif

#ifdef JAVA
	jobject token_to_java (JavaEnvironment *env, jobject attrs, jobject sloc);
#endif

#ifdef CLR
	Tycho::Parser::Tokens::Token ^token_to_clr (System::Collections::Generic::List<Tycho::Parser::Tokens::Token^> ^attrs, Tycho::Parser::Tokens::SourceLocation ^sloc);
#endif
};

class Line {
public:
	char *_line;
	char *_whole_line;
	uint _indent;
	bool _indent_error;
	Line *_next;

	Line (int indent, char *whole_line, char *line, bool indent_error);
	~Line ();
	void set_indent (int indentation);
	void indent (int indentation);
	uint get_total_length ();
	void build_content (char *content, bool add_newline);
	Line *clone ();
};

class Block;

class TokenLocation {
public:
	char *_line_start, *_token_start;
	int _lineno, _colno;
	char *_source, *_file_name;

	SourceLocation *calc_location (char *line);
	SourceLocation *calc_location (char *line, uint end_lineno, char *line_start);
	static SourceLocation *new_location (char *source, char *file_name, uint line, uint col);
	static SourceLocation *new_location (char *source, char *file_name, uint line_start, uint line_end, uint col_start, uint col_end);
};

class Tokens {
	bool _newline, _has_tokens;
	int _new_line_lineno, _new_line_indent, _new_line_colno;
	bool _awaiting_attribute, _indent_block_last;
	SourceLocation *_newline_sloc,
				   *_attribute_sloc,
				   *_identifier_sloc,
				   *_module_delim_sloc;

	bool _block_first;
	vector<Token*> *_tokens;
	char *_last_identifier;
	vector<const char*> *_module_path;
	vector<Token*> *_attributes;

	ListToken *_list_token;

	void push_proper (Token *tok);
	void push_identifier_proper ();
	void push_newline_proper ();
	void check_module_path ();
	void set_module (IdentifierToken *id);
	void set_attributes (Token *token);
	void expecting_attribute ();
	void check_attributes ();

	void clear_tokens ();
	void clear_attributes ();
	void clear_module_path ();
public:
	Tokens ();
	~Tokens ();

	SourceLocation *_sloc;

	void push_identifier (char *id, SourceLocation *sloc);
	void push (Token *tok, SourceLocation *sloc);
	void push_block (Tokens *block, SourceLocation *sloc, bool indent_block);
	void push_newline (SourceLocation *sloc);
	void push_module_delim (SourceLocation *sloc);
	void await_attribute (SourceLocation *sloc);

	void flush ();
	vector<Token*> *get_tokens ();

	ListToken *get_list_token (ListToken::Type type);
};

class ParserTokens {
	vector<Token*> *_tokens;

public:
	ParserTokens ();

	SourceLocation *_sloc;

	void push (Token *tok, SourceLocation *sloc);
	ListToken *get_list_token (ListToken::Type type);
};

class NewlineParser : public ParserTokens {
	bool _newline, _has_tokens;
	SourceLocation *_newline_sloc;

public:
	NewlineParser ();

	void push_newline (SourceLocation *sloc);
	void push (Token *tok, SourceLocation *sloc);
};

class ModuleParser : public NewlineParser {
	SourceLocation *_identifier_sloc,
				   *_module_delim_sloc;
	char *_last_identifier;
	vector<const char*> *_module_path;

	void push_identifier_proper ();
public:
	ModuleParser ();

	void push_identifier (char *id, SourceLocation *sloc);
	void push_module_delim (SourceLocation *sloc);
	void push (Token *tok, SourceLocation *sloc);
	void flush ();
};

class AttributeParser : public ModuleParser {
	bool _awaiting_attribute;
	SourceLocation *_attribute_sloc;
	vector<Token*> *_attributes;

	void check_awaiting_attribute ();
public:
	AttributeParser ();

	void await_attribute (SourceLocation *sloc);
	void push (Token *tok, SourceLocation *sloc);
	void flush ();
};

class BlockParser;

typedef Tokens Parser;

class BlockParser : public AttributeParser {
	SourceLocation *_solitair_block_sloc;
	Parser *_solitair_block;
	bool _has_tokens;

public:
	BlockParser ();

	void push_block (Parser *block, SourceLocation *sloc);
	void push (Token *tok, SourceLocation *sloc);
	void flush ();
	ListToken *get_list_token (ListToken::Type type);
};

class Lexer {
	int _lineno, _colno;
	char *_line_start;
	Line *_lines;
	char *_source, *_file_name;

	void consume_identifier (Parser *parser, TokenLocation *token_loc, char **line);

	void consume_operator (Parser *parser, TokenLocation *token_loc, char **line);

	void consume_multiline_quote (Parser *parser, char **line, TokenLocation *token_loc);
	void consume_quote (Parser *parser, TokenLocation *token_loc, char **line);
	void consume_quote_string (Parser *parser, TokenLocation *token_loc, char **line, char *label, char end, bool escaped);

	void consume_number (Parser *parser, TokenLocation *token_loc, char **line);
	void consume_decimal_number (Parser *parser, TokenLocation *token_loc, char **line);
	void consume_hex_number (Parser *parser, TokenLocation *token_loc, char **line);
	void consume_octal_number (Parser *parser, TokenLocation *token_loc, char **line);
	void consume_binary_number (Parser *parser, TokenLocation *token_loc, char **line);

	void consume_floating_number (Parser *parser, TokenLocation *token_loc, char **line, long integral_part);
	void consume_version (Parser *parser, TokenLocation *token_loc, char **line);

	bool consume_suffix (Parser *parser, TokenLocation *token_loc, char **line, long integer);
	void consume_dots (Parser *parser, TokenLocation *token_loc, char **line);

	void consume_multiline_comment (Parser *parser, char **line, TokenLocation *token_loc);

	void next_line ();
	void reset_counters (Parser *parser, TokenLocation *token_loc, char bracket_end);
	char *get_indented_block (uint indent);
	char *get_quote_block ();

public:
	Lexer (Block *block);
	void lex (Parser *parser, char **line, char bracket_end, bool in_block, uint min_indent, uint indent, bool single_token);
	void interpolate (vector<Token*> *items);
};

class Block {
public:
	int _size;
	Line *_first, *_last;
	int _lineno, _colno;
	char *_source;
	char *_file_name;

	struct Indent {
		const char *indent;
		uint len;
	};

	vector<Indent> _indents;
	const char *_indent;
	uint _indent_len;

	uint get_indent (const char *line, uint *n_indent_chars, bool *error);
	void consume (const char *input);
	void clear ();
	char *get_line (int n);

	void set_indent (int indentation);
	void indent (int indentation);
	char *get_content (bool add_newline);
	void add_line (int indent, char *whole_line, char *line, bool indent_error);
	void add_line (Line *indent_line);

	void construct ();

	Block ();
	~Block ();
	void lex (Parser *parser, char bracket_end, bool in_block);
	void lex (Parser *parser);

#ifdef RUBY
	VALUE to_ruby ();
#endif
};

#define INTERPOLATION_ESCAPE '#'

char *make_line (const char *start, const char *end);

void init_tokenizer ();

#ifdef RUBY
extern VALUE cBlock;
extern VALUE tt_brace, tt_parenthesis, tt_list, tt_bracket;

extern VALUE sNew;
extern VALUE cSourceLocation;
extern VALUE cTokens;
extern VALUE cIdentifierToken;
extern VALUE cIntegerToken;
extern VALUE cFloatingToken;
extern VALUE cStringToken;
extern VALUE cQuoteToken;
extern VALUE cErrorToken;
extern VALUE cTokenList;
extern VALUE cVersionToken;
extern VALUE cVersion;

extern VALUE sl_type_file, sl_type_source;

extern ID iPush;
#endif

#endif
